/*
 ------------------------------------------------------------------------------
 Copyright (C) 2013 Eternal Games.

 This file is part of the GLQuake source code.

 The GLQuake source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The GLQuake source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the GLQuake source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


//
// cg_deltaState.cpp - parses a delta state received from the server-game
//


#include "cg_local.h"


/*
 ==================
 CG_ParseDeltaState_General
 ==================
*/
static void CG_ParseDeltaState_General (msg_t *msg, entityState_t *oldState, entityState_t *newState){

	general_t	*oldGeneral = (general_t *)oldState->buffer;
	general_t	*newGeneral = (general_t *)newState->buffer;

	newGeneral->contents = glqMsgSystem->ReadDeltaLong(msg, oldGeneral->contents);
	newGeneral->bounds = glqMsgSystem->ReadDeltaLong(msg, oldGeneral->bounds);
	newGeneral->model = glqMsgSystem->ReadDeltaBits(msg, oldGeneral->model, ES_MODEL_BITS);
	newGeneral->clipModel = glqMsgSystem->ReadDeltaBits(msg, oldGeneral->clipModel, ES_CLIP_MODEL_BITS);
	newGeneral->material = glqMsgSystem->ReadDeltaBits(msg, oldGeneral->material, ES_MATERIAL_BITS);
	newGeneral->materialParms[0] = glqMsgSystem->ReadDeltaFloat(msg, oldGeneral->materialParms[0]);
	newGeneral->materialParms[1] = glqMsgSystem->ReadDeltaFloat(msg, oldGeneral->materialParms[1]);
	newGeneral->materialParms[2] = glqMsgSystem->ReadDeltaFloat(msg, oldGeneral->materialParms[2]);
	newGeneral->materialParms[3] = glqMsgSystem->ReadDeltaFloat(msg, oldGeneral->materialParms[3]);
	newGeneral->materialParms[4] = glqMsgSystem->ReadDeltaFloat(msg, oldGeneral->materialParms[4]);
	newGeneral->materialParms[5] = glqMsgSystem->ReadDeltaFloat(msg, oldGeneral->materialParms[5]);
	newGeneral->materialParms[6] = glqMsgSystem->ReadDeltaFloat(msg, oldGeneral->materialParms[6]);
	newGeneral->materialParms[7] = glqMsgSystem->ReadDeltaFloat(msg, oldGeneral->materialParms[7]);
	newGeneral->sound = glqMsgSystem->ReadDeltaBits(msg, oldGeneral->sound, ES_SOUND_SHADER_BITS);
	newGeneral->soundTime = glqMsgSystem->ReadDeltaLong(msg, oldGeneral->soundTime);
	newGeneral->soundParms[0] = glqMsgSystem->ReadDeltaFloat(msg, oldGeneral->soundParms[0]);
	newGeneral->soundParms[1] = glqMsgSystem->ReadDeltaFloat(msg, oldGeneral->soundParms[1]);
	newGeneral->soundParms[2] = glqMsgSystem->ReadDeltaFloat(msg, oldGeneral->soundParms[2]);
	newGeneral->soundParms[3] = glqMsgSystem->ReadDeltaFloat(msg, oldGeneral->soundParms[3]);
	newGeneral->soundParms[4] = glqMsgSystem->ReadDeltaFloat(msg, oldGeneral->soundParms[4]);
	newGeneral->soundParms[5] = glqMsgSystem->ReadDeltaFloat(msg, oldGeneral->soundParms[5]);
	newGeneral->soundParms[6] = glqMsgSystem->ReadDeltaFloat(msg, oldGeneral->soundParms[6]);
	newGeneral->soundParms[7] = glqMsgSystem->ReadDeltaFloat(msg, oldGeneral->soundParms[7]);
}

/*
 ==================
 
 ==================
*/
static void CG_ParseDeltaState_Mover (msg_t *msg, entityState_t *oldState, entityState_t *newState){

	mover_t	*oldMover = (mover_t *)oldState->buffer;
	mover_t	*newMover = (mover_t *)newState->buffer;

	newMover->model = glqMsgSystem->ReadDeltaBits(msg, oldMover->model, ES_MODEL_BITS);
}

/*
 ==================
 
 ==================
*/
static void CG_ParseDeltaState_Beam (msg_t *msg, entityState_t *oldState, entityState_t *newState){

}

/*
 ==================
 CG_ParseDeltaState_Light
 ==================
*/
static void CG_ParseDeltaState_Light (msg_t *msg, entityState_t *oldState, entityState_t *newState){

	light_t	*oldLight = (light_t *)oldState->buffer;
	light_t	*newLight = (light_t *)newState->buffer;

	newLight->type = glqMsgSystem->ReadDeltaBits(msg, oldLight->type, 2);
	newLight->center = glqMsgSystem->ReadDeltaVec3(msg, oldLight->center);
	newLight->radius = glqMsgSystem->ReadDeltaVec3(msg, oldLight->radius);
	newLight->xMin = glqMsgSystem->ReadDeltaFloat(msg, oldLight->xMin);
	newLight->xMax = glqMsgSystem->ReadDeltaFloat(msg, oldLight->xMax);
	newLight->yMin = glqMsgSystem->ReadDeltaFloat(msg, oldLight->yMin);
	newLight->yMax = glqMsgSystem->ReadDeltaFloat(msg, oldLight->yMax);
	newLight->zNear = glqMsgSystem->ReadDeltaFloat(msg, oldLight->zNear);
	newLight->zFar = glqMsgSystem->ReadDeltaFloat(msg, oldLight->zFar);
	newLight->noShadows = glqMsgSystem->ReadBool(msg);
	newLight->shadowBias = glqMsgSystem->ReadDeltaFloat(msg, oldLight->shadowBias);
	newLight->shadowSlopeScaleBias = glqMsgSystem->ReadDeltaFloat(msg, oldLight->shadowSlopeScaleBias);
	newLight->shadowSoftness = glqMsgSystem->ReadDeltaFloat(msg, oldLight->shadowSoftness);
	newLight->fogDistance = glqMsgSystem->ReadDeltaFloat(msg, oldLight->fogDistance);
	newLight->fogHeight = glqMsgSystem->ReadDeltaFloat(msg, oldLight->fogHeight);
	newLight->volumeIntensity = glqMsgSystem->ReadDeltaFloat(msg, oldLight->volumeIntensity);
	newLight->volumeShadows = glqMsgSystem->ReadBool(msg);
	newLight->volumeInteractions = glqMsgSystem->ReadBool(msg);
	newLight->detailLevel = glqMsgSystem->ReadDeltaBits(msg, oldLight->detailLevel, 2);
	newLight->material = glqMsgSystem->ReadDeltaBits(msg, oldLight->material, ES_MATERIAL_BITS);
	newLight->materialParms[0] = glqMsgSystem->ReadDeltaFloat(msg, oldLight->materialParms[0]);
	newLight->materialParms[1] = glqMsgSystem->ReadDeltaFloat(msg, oldLight->materialParms[1]);
	newLight->materialParms[2] = glqMsgSystem->ReadDeltaFloat(msg, oldLight->materialParms[2]);
	newLight->materialParms[3] = glqMsgSystem->ReadDeltaFloat(msg, oldLight->materialParms[3]);
	newLight->materialParms[4] = glqMsgSystem->ReadDeltaFloat(msg, oldLight->materialParms[4]);
	newLight->materialParms[5] = glqMsgSystem->ReadDeltaFloat(msg, oldLight->materialParms[5]);
	newLight->materialParms[6] = glqMsgSystem->ReadDeltaFloat(msg, oldLight->materialParms[6]);
	newLight->materialParms[7] = glqMsgSystem->ReadDeltaFloat(msg, oldLight->materialParms[7]);
}

/*
 ==================
 CG_ParseDeltaState_Speaker
 ==================
*/
static void CG_ParseDeltaState_Speaker (msg_t *msg, entityState_t *oldState, entityState_t *newState){

	speaker_t	*oldSpeaker = (speaker_t *)oldState->buffer;
	speaker_t	*newSpeaker = (speaker_t *)newState->buffer;

	newSpeaker->direction = glqMsgSystem->ReadDeltaNormal(msg, oldSpeaker->direction);
	newSpeaker->sound = glqMsgSystem->ReadDeltaBits(msg, oldSpeaker->sound, ES_SOUND_SHADER_BITS);
	newSpeaker->soundTime = glqMsgSystem->ReadDeltaLong(msg, oldSpeaker->soundTime);
	newSpeaker->soundParms[0] = glqMsgSystem->ReadDeltaFloat(msg, oldSpeaker->soundParms[0]);
	newSpeaker->soundParms[1] = glqMsgSystem->ReadDeltaFloat(msg, oldSpeaker->soundParms[1]);
	newSpeaker->soundParms[2] = glqMsgSystem->ReadDeltaFloat(msg, oldSpeaker->soundParms[2]);
	newSpeaker->soundParms[3] = glqMsgSystem->ReadDeltaFloat(msg, oldSpeaker->soundParms[3]);
	newSpeaker->soundParms[4] = glqMsgSystem->ReadDeltaFloat(msg, oldSpeaker->soundParms[4]);
	newSpeaker->soundParms[5] = glqMsgSystem->ReadDeltaFloat(msg, oldSpeaker->soundParms[5]);
	newSpeaker->soundParms[6] = glqMsgSystem->ReadDeltaFloat(msg, oldSpeaker->soundParms[6]);
	newSpeaker->soundParms[7] = glqMsgSystem->ReadDeltaFloat(msg, oldSpeaker->soundParms[7]);
}

/*
 ==================
 CG_ParseDeltaState_Player
 ==================
*/
static void CG_ParseDeltaState_Player (msg_t *msg, entityState_t *oldState, entityState_t *newState){

	player_t	*oldPlayer = (player_t *)oldState->buffer;
	player_t	*newPlayer = (player_t *)newState->buffer;

	newPlayer->bounds = glqMsgSystem->ReadDeltaLong(msg, oldPlayer->bounds);

	newPlayer->model = glqMsgSystem->ReadDeltaBits(msg, oldPlayer->model, ES_MODEL_BITS);

	newPlayer->sound = glqMsgSystem->ReadDeltaBits(msg, oldPlayer->sound, ES_SOUND_SHADER_BITS);
	newPlayer->soundTime = glqMsgSystem->ReadDeltaLong(msg, oldPlayer->soundTime);
	newPlayer->soundParms[0] = glqMsgSystem->ReadDeltaFloat(msg, oldPlayer->soundParms[0]);
	newPlayer->soundParms[1] = glqMsgSystem->ReadDeltaFloat(msg, oldPlayer->soundParms[1]);
	newPlayer->soundParms[2] = glqMsgSystem->ReadDeltaFloat(msg, oldPlayer->soundParms[2]);
	newPlayer->soundParms[3] = glqMsgSystem->ReadDeltaFloat(msg, oldPlayer->soundParms[3]);
	newPlayer->soundParms[4] = glqMsgSystem->ReadDeltaFloat(msg, oldPlayer->soundParms[4]);
	newPlayer->soundParms[5] = glqMsgSystem->ReadDeltaFloat(msg, oldPlayer->soundParms[5]);
	newPlayer->soundParms[6] = glqMsgSystem->ReadDeltaFloat(msg, oldPlayer->soundParms[6]);
	newPlayer->soundParms[7] = glqMsgSystem->ReadDeltaFloat(msg, oldPlayer->soundParms[7]);
}

/*
 ==================
 
 ==================
*/
static void CG_ParseDeltaState_Item (msg_t *msg, entityState_t *oldState, entityState_t *newState){

	item_t	*oldItem = (item_t *)oldState->buffer;
	item_t	*newItem = (item_t *)newState->buffer;

	newItem->bounds = glqMsgSystem->ReadDeltaLong(msg, oldItem->bounds);

	newItem->model = glqMsgSystem->ReadDeltaBits(msg, oldItem->model, ES_MODEL_BITS);

	newItem->sound = glqMsgSystem->ReadDeltaBits(msg, oldItem->sound, ES_SOUND_SHADER_BITS);
	newItem->soundTime = glqMsgSystem->ReadDeltaLong(msg, oldItem->soundTime);
	newItem->soundParms[0] = glqMsgSystem->ReadDeltaFloat(msg, oldItem->soundParms[0]);
	newItem->soundParms[1] = glqMsgSystem->ReadDeltaFloat(msg, oldItem->soundParms[1]);
	newItem->soundParms[2] = glqMsgSystem->ReadDeltaFloat(msg, oldItem->soundParms[2]);
	newItem->soundParms[3] = glqMsgSystem->ReadDeltaFloat(msg, oldItem->soundParms[3]);
	newItem->soundParms[4] = glqMsgSystem->ReadDeltaFloat(msg, oldItem->soundParms[4]);
	newItem->soundParms[5] = glqMsgSystem->ReadDeltaFloat(msg, oldItem->soundParms[5]);
	newItem->soundParms[6] = glqMsgSystem->ReadDeltaFloat(msg, oldItem->soundParms[6]);
	newItem->soundParms[7] = glqMsgSystem->ReadDeltaFloat(msg, oldItem->soundParms[7]);
}

/*
 ==================
 
 ==================
*/
static void CG_ParseDeltaState_Monster (msg_t *msg, entityState_t *oldState, entityState_t *newState){

	monster_t	*oldMonster = (monster_t *)oldState->buffer;
	monster_t	*newMonster = (monster_t *)newState->buffer;

	newMonster->bounds = glqMsgSystem->ReadDeltaLong(msg, oldMonster->bounds);

	newMonster->model = glqMsgSystem->ReadDeltaBits(msg, oldMonster->model, ES_MODEL_BITS);

	newMonster->sound = glqMsgSystem->ReadDeltaBits(msg, oldMonster->sound, ES_SOUND_SHADER_BITS);
	newMonster->soundTime = glqMsgSystem->ReadDeltaLong(msg, oldMonster->soundTime);
	newMonster->soundParms[0] = glqMsgSystem->ReadDeltaFloat(msg, oldMonster->soundParms[0]);
	newMonster->soundParms[1] = glqMsgSystem->ReadDeltaFloat(msg, oldMonster->soundParms[1]);
	newMonster->soundParms[2] = glqMsgSystem->ReadDeltaFloat(msg, oldMonster->soundParms[2]);
	newMonster->soundParms[3] = glqMsgSystem->ReadDeltaFloat(msg, oldMonster->soundParms[3]);
	newMonster->soundParms[4] = glqMsgSystem->ReadDeltaFloat(msg, oldMonster->soundParms[4]);
	newMonster->soundParms[5] = glqMsgSystem->ReadDeltaFloat(msg, oldMonster->soundParms[5]);
	newMonster->soundParms[6] = glqMsgSystem->ReadDeltaFloat(msg, oldMonster->soundParms[6]);
	newMonster->soundParms[7] = glqMsgSystem->ReadDeltaFloat(msg, oldMonster->soundParms[7]);
}

/*
 ==================
 
 ==================
*/
static void CG_ParseDeltaState_Projectile (msg_t *msg, entityState_t *oldState, entityState_t *newState){

	projectile_t	*oldProjectile = (projectile_t *)oldState->buffer;
	projectile_t	*newProjectile = (projectile_t *)newState->buffer;
}

/*
 ==================
 
 ==================
*/
static void CG_ParseDeltaState (msg_t *msg, entityState_t *oldState, entityState_t *newState){

	int		i;

	// Parse the position and orientation
	newState->origin.type = glqMsgSystem->ReadDeltaByte(msg, oldState->origin.type);
	newState->origin.time = glqMsgSystem->ReadDeltaLong(msg, oldState->origin.time);
	newState->origin.duration = glqMsgSystem->ReadDeltaLong(msg, oldState->origin.duration);
	newState->origin.base = glqMsgSystem->ReadDeltaVec3(msg, oldState->origin.base);
	newState->origin.delta = glqMsgSystem->ReadDeltaVec3(msg, oldState->origin.delta);

	newState->angles.type = glqMsgSystem->ReadDeltaByte(msg, oldState->angles.type);
	newState->angles.time = glqMsgSystem->ReadDeltaLong(msg, oldState->angles.time);
	newState->angles.duration = glqMsgSystem->ReadDeltaLong(msg, oldState->angles.duration);
	newState->angles.base = glqMsgSystem->ReadDeltaAngles(msg, oldState->angles.base);
	newState->angles.delta = glqMsgSystem->ReadDeltaAngles(msg, oldState->angles.delta);

	// Parse the binding information
	newState->bindMaster = glqMsgSystem->ReadDeltaBits(msg, oldState->bindMaster, ES_BIND_MASTER_BITS);
	newState->bindOrientated = glqMsgSystem->ReadBool(msg);

	// Parse the event queue
	newState->eventSequence = glqMsgSystem->ReadDeltaLong(msg, oldState->eventSequence);

	for (i = 0; i < MAX_EVENTS; i++){
		newState->eventTypes[i] = glqMsgSystem->ReadDeltaByte(msg, oldState->eventTypes[i]);
		newState->eventParms[i] = glqMsgSystem->ReadDeltaWord(msg, oldState->eventParms[i]);
	}

	// Parse the state buffer
	switch (newState->type){
	case ET_GENERAL:
		CG_ParseDeltaState_General(msg, oldState, newState);
		break;
	case ET_MOVER:
		CG_ParseDeltaState_Mover(msg, oldState, newState);
		break;
	case ET_BEAM:
		CG_ParseDeltaState_Beam(msg, oldState, newState);
		break;
	case ET_LIGHT:
		CG_ParseDeltaState_Light(msg, oldState, newState);
		break;
	case ET_SPEAKER:
		CG_ParseDeltaState_Speaker(msg, oldState, newState);
		break;
	case ET_PLAYER:
		CG_ParseDeltaState_Player(msg, oldState, newState);
		break;
	case ET_ITEM:
		CG_ParseDeltaState_Item(msg, oldState, newState);
		break;
	case ET_MONSTER:
		CG_ParseDeltaState_Monster(msg, oldState, newState);
		break;
	case ET_PROJECTILE:
		CG_ParseDeltaState_Projectile(msg, oldState, newState);
		break;
	default:
		G_Error("CG_ParseDeltaState: bad entity type (%i)", newState->type);
	}
}


/*
 ==============================================================================

 PLAYER AND ENTITY STATES

 ==============================================================================
*/


/*
 ==================
 
 ==================
*/
void CG_ParseDeltaPlayerState (msg_t *msg, playerState_t *oldState, playerState_t *newState){

	playerState_t	nullState;
	int				i;

	if (!oldState){
		oldState = &nullState;
		glqMemory->Fill(oldState, 0, sizeof(playerState_t));
	}

	newState->pmType = glqMsgSystem->ReadDeltaBits(msg, oldState->pmType, PS_MOVE_TYPE_BITS);
	newState->pmFlags = glqMsgSystem->ReadDeltaBits(msg, oldState->pmFlags, PS_MOVE_FLAGS_BITS);
	newState->pmTime = glqMsgSystem->ReadDeltaBits(msg, oldState->pmTime, PS_MOVE_TIME_BITS);
	newState->clientNum = glqMsgSystem->ReadDeltaBits(msg, oldState->clientNum, 6);
	newState->flags = glqMsgSystem->ReadDeltaBits(msg, oldState->flags, ES_FLAGS_BITS);
	newState->origin = glqMsgSystem->ReadDeltaVec3(msg, oldState->origin);
	newState->velocity = glqMsgSystem->ReadDeltaVec3(msg, oldState->velocity);
	newState->bounds = glqMsgSystem->ReadDeltaLong(msg, oldState->bounds);
	newState->viewHeight = glqMsgSystem->ReadDeltaFloat(msg, oldState->viewHeight);
	newState->weaponType = glqMsgSystem->ReadDeltaLong(msg, oldState->weaponType);
	newState->weaponFrame = glqMsgSystem->ReadDeltaLong(msg, oldState->weaponFrame);
	newState->viewAngles = glqMsgSystem->ReadDeltaAngles16(msg, oldState->viewAngles);
	newState->deltaAngles = glqMsgSystem->ReadDeltaAngles16(msg, oldState->deltaAngles);
	newState->userCmdTime = glqMsgSystem->ReadDeltaLong(msg, oldState->userCmdTime);
	newState->groundEntityNum = glqMsgSystem->ReadDeltaBits(msg, oldState->groundEntityNum, ES_NUMBER_BITS);

	newState->eventSequence = glqMsgSystem->ReadDeltaLong(msg, oldState->eventSequence);

	for (i = 0; i < MAX_EVENTS; i++){
		newState->eventTypes[i] = glqMsgSystem->ReadDeltaByte(msg, oldState->eventTypes[i]);
		newState->eventParms[i] = glqMsgSystem->ReadDeltaWord(msg, oldState->eventParms[i]);
	}
}

/*
 ==================
 CG_ParseDeltaEntityState
 ==================
*/
void CG_ParseDeltaEntityState (msg_t *msg, entityState_t *oldState, entityState_t *newState, int number){

	// Check for a delta remove
	if (glqMsgSystem->ReadBits(msg, 1) == 1){
		glqMemory->Fill(newState, 0, sizeof(entityState_t));
		newState->number = ENTITYNUM_NONE;

		return;
	}

	// Check for no delta
	if (glqMsgSystem->ReadBits(msg, 1) == 0){
		glqMemory->Copy(newState, oldState, sizeof(entityState_t));
		newState->number = number;

		return;
	}

	newState->number = number;
	newState->type = glqMsgSystem->ReadBits(msg, ES_TYPE_BITS);
	newState->flags = glqMsgSystem->ReadBits(msg, ES_FLAGS_BITS);

	newState->weaponType = glqMsgSystem->ReadBits(msg, ES_FLAGS_BITS + 5);		// FIXME!!!

	newState->frame = glqMsgSystem->ReadBits(msg, ES_FLAGS_BITS + 10);		// FIXME!!!

	// Delta uncompress the entity state
	CG_ParseDeltaState(msg, oldState, newState);
}